#include <string.h>
#include <sys/param.h>
#include <stdlib.h>
#include <ctype.h>
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "protocol_examples_utils.h"
#include "esp_tls.h"
#include "esp_crt_bundle.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_http_client.h"
#include "driver/gpio.h"
#include "cJson.h"

#include "led_strip.h"

// led灯带配置
#define LED_STRIP_GPIO_PIN  1
// Numbers of the LED in the strip
#define LED_STRIP_LED_COUNT 12
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
#define LED_STRIP_RMT_RES_HZ  (10 * 1000 * 1000)

// rgb色彩
static uint8_t base_r =150;
static uint8_t base_g =150;
static uint8_t base_b =150;
static int led_mode = 3;

static int direction = 2;
static int brightness = 0;
static char* result_text;


#define GPIO_INPUT_PIN GPIO_NUM_0
#define MAX_HTTP_OUTPUT_BUFFER 2048

static const char *TAG = "HTTP_CLIENT";

static void http_test_task(void *pvParameters);
static void http_rest_with_url(void);
esp_err_t _http_event_handler(esp_http_client_event_t *evt);
static void led_task(void *pvParameters);
led_strip_handle_t configure_led(void);
void led_breath(led_strip_handle_t led_strip);
void led_flow(led_strip_handle_t led_strip);
void led_breath_colorChange(led_strip_handle_t led_strip);
void led_rainbow_rotate(led_strip_handle_t led_strip);

// 全局变量声明
static char* city_name;
static char* weather;
static char* temperature;
SemaphoreHandle_t data_ready;
SemaphoreHandle_t json_done;

void app_main(void)
{
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(example_connect());
    ESP_LOGI(TAG, "网络连接成功!");
    // 创建二进制信号量
    data_ready = xSemaphoreCreateBinary();
    json_done = xSemaphoreCreateBinary();

    // 开启一个任务
    xTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL);
    xTaskCreate(&led_task, "led_task", 8192, NULL, 5, NULL);
}

static void http_test_task(void *pvParameters)
{
  while (1)
  {
    if(gpio_get_level(GPIO_INPUT_PIN) == 0)
    {
      http_rest_with_url();
      vTaskDelay(pdMS_TO_TICKS(100));
    }
    vTaskDelay(pdMS_TO_TICKS(100));
  }
  vTaskDelete(NULL);
}

static void http_rest_with_url(void)
{
    char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER + 1] = {0};

    esp_http_client_config_t config = {
        .url = "http://api.seniverse.com/v3/weather/now.json?key=SnvaF-iCsqnqB0lcz&location=%E6%B9%9B%E6%B1%9F&language=zh-Hans&unit=c",
        .event_handler = _http_event_handler,
        .user_data = local_response_buffer,
        .disable_auto_redirect = true,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);

    // GET
    esp_err_t err = esp_http_client_perform(client);
    if (err == ESP_OK) {
      ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %"PRId64,
              esp_http_client_get_status_code(client),
              esp_http_client_get_content_length(client));
    } else {
        ESP_LOGE(TAG, "GET请求失败: %s", esp_err_to_name(err));
    }
    cJSON *root = cJSON_Parse(local_response_buffer);
    result_text = local_response_buffer;
    if (root == NULL) {
        ESP_LOGE(TAG, "JSON 解析失败");
    } else {
        // 获取 results 数组
        cJSON *results = cJSON_GetObjectItem(root, "results");
        if (results && cJSON_IsArray(results)) {
            cJSON *first_result = cJSON_GetArrayItem(results, 0);
            if (first_result) {
                cJSON *location = cJSON_GetObjectItem(first_result, "location");
                city_name = cJSON_GetObjectItem(location, "name")->valuestring;
                cJSON *now = cJSON_GetObjectItem(first_result, "now");
                weather = cJSON_GetObjectItem(now, "text")->valuestring;
                temperature = cJSON_GetObjectItem(now, "temperature")->valuestring;
                ESP_LOGI(TAG, "城市: %s", city_name);
                ESP_LOGI(TAG, "天气: %s, 温度: %s°C", weather,temperature);
                xSemaphoreGive(data_ready);
            }
        }
        // 等待json对象用完再释放
        while (xSemaphoreTake(json_done, pdMS_TO_TICKS(10)) != pdTRUE)
        {
          vTaskDelay(pdMS_TO_TICKS(100));
        }
        
        cJSON_Delete(root);
    }
    // ESP_LOGI(TAG, "%s", local_response_buffer);
    esp_http_client_cleanup(client);
}

esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    static char *output_buffer;  // Buffer to store response of http request from event handler
    static int output_len;       // Stores number of bytes read
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:
            ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            // Clean the buffer in case of a new request
            if (output_len == 0 && evt->user_data) {
                // we are just starting to copy the output data into the use
                memset(evt->user_data, 0, MAX_HTTP_OUTPUT_BUFFER);
            }
            /*
             *  Check for chunked encoding is added as the URL for chunked encoding used in this example returns binary data.
             *  However, event handler can also be used in case chunked encoding is used.
             */
            if (!esp_http_client_is_chunked_response(evt->client)) {
                // If user_data buffer is configured, copy the response into the buffer
                int copy_len = 0;
                if (evt->user_data) {
                    // The last byte in evt->user_data is kept for the NULL character in case of out-of-bound access.
                    copy_len = MIN(evt->data_len, (MAX_HTTP_OUTPUT_BUFFER - output_len));
                    if (copy_len) {
                        memcpy(evt->user_data + output_len, evt->data, copy_len);
                    }
                } else {
                    int content_len = esp_http_client_get_content_length(evt->client);
                    if (output_buffer == NULL) {
                        // We initialize output_buffer with 0 because it is used by strlen() and similar functions therefore should be null terminated.
                        output_buffer = (char *) calloc(content_len + 1, sizeof(char));
                        output_len = 0;
                        if (output_buffer == NULL) {
                            ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
                            return ESP_FAIL;
                        }
                    }
                    copy_len = MIN(evt->data_len, (content_len - output_len));
                    if (copy_len) {
                        memcpy(output_buffer + output_len, evt->data, copy_len);
                    }
                }
                output_len += copy_len;
            }

            break;
        case HTTP_EVENT_ON_FINISH:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
            if (output_buffer != NULL) {
                // Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response
                // ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);
                free(output_buffer);
                output_buffer = NULL;
            }
            output_len = 0;
            break;
        case HTTP_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
            int mbedtls_err = 0;
            esp_err_t err = esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, &mbedtls_err, NULL);
            if (err != 0) {
                ESP_LOGI(TAG, "Last esp error code: 0x%x", err);
                ESP_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
            }
            if (output_buffer != NULL) {
                free(output_buffer);
                output_buffer = NULL;
            }
            output_len = 0;
            break;
        case HTTP_EVENT_REDIRECT:
            ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
            esp_http_client_set_header(evt->client, "From", "user@example.com");
            esp_http_client_set_header(evt->client, "Accept", "text/html");
            esp_http_client_set_redirection(evt->client);
            break;
    }
    return ESP_OK;
}

static void led_task(void *pvParameters){
  led_strip_handle_t led_strip = configure_led();
  while(1){
    if (xSemaphoreTake(data_ready, pdMS_TO_TICKS(10)) == pdTRUE)
    {
      if (strcmp(weather, "晴") == 0) {
        led_mode = 3;
      } else if (strcmp(weather, "阴") == 0) {
        led_mode = 0;
      }else if (strcmp(weather, "多云") == 0){
        led_mode = 1;
      }else{
        led_mode = 2;
      }
      xSemaphoreGive(json_done);
    }
    switch(led_mode){
      case 0:
        led_breath(led_strip);
        break;
      case 1:
        led_flow(led_strip);
        break;
      case 2:
        led_breath_colorChange(led_strip);
        break;
      case 3:
        led_rainbow_rotate(led_strip);
        break;
    }
  }
  vTaskDelete(NULL);
}

led_strip_handle_t configure_led(void)
{
    // LED strip general initialization, according to your led board design
    led_strip_config_t strip_config = {
        .strip_gpio_num = LED_STRIP_GPIO_PIN, // The GPIO that connected to the LED strip's data line
        .max_leds = LED_STRIP_LED_COUNT,      // The number of LEDs in the strip,
        .led_model = LED_MODEL_WS2812,        // LED strip model
        .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color order of the strip: GRB
        .flags = {
            .invert_out = false, // don't invert the output signal
        }
    };

    // LED strip backend configuration: RMT
    led_strip_rmt_config_t rmt_config = {
        .clk_src = RMT_CLK_SRC_DEFAULT,        // different clock source can lead to different power consumption
        .resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency
        .mem_block_symbols = 64,               // the memory size of each RMT channel, in words (4 bytes)
        .flags = {
            .with_dma = false, // DMA feature is available on chips like ESP32-S3/P4
        }
    };

    // LED Strip object handle
    led_strip_handle_t led_strip;
    ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
    ESP_LOGI(TAG, "Created LED strip object with RMT backend");
    return led_strip;
}

void led_breath(led_strip_handle_t led_strip){
  uint8_t color_r = base_r * brightness / 255;
  uint8_t color_g = base_g * brightness / 255;
  uint8_t color_b = base_b * brightness / 255;
  for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
    ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i,color_r , color_g, color_b));
  }
  ESP_ERROR_CHECK(led_strip_refresh(led_strip));

  brightness += direction;
  if (brightness >= 255 || brightness <= 0) {
    direction = -direction;
  }
  vTaskDelay(pdMS_TO_TICKS(10));
}

void led_flow(led_strip_handle_t led_strip){
  int tail_length = 3;
  for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
    ESP_ERROR_CHECK(led_strip_clear(led_strip));
    for (int j = 0; j < tail_length; j++) {
      int position = i - j;
      if (position >= 0 && position < LED_STRIP_LED_COUNT) {  // 确保 position 在有效范围内
        uint8_t color_r = base_r - (j * (base_r / tail_length));
        uint8_t color_g = base_g - (j * (base_g / tail_length));
        uint8_t color_b = base_b - (j * (base_b / tail_length));
        ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, position, color_r, color_g, color_b));
      }
    }
    ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, base_r, base_g, base_b));
    ESP_ERROR_CHECK(led_strip_refresh(led_strip));
    vTaskDelay(pdMS_TO_TICKS(100));
  }
}

void led_breath_colorChange(led_strip_handle_t led_strip){
  // 动态调整 RGB 颜色值
  static uint8_t color_phase = 0;  // 颜色变化相位
  static uint8_t color_step = 1;  // 颜色变化步长

  // 根据相位计算 RGB 值
  uint8_t color_r = (color_phase < 85) ? (255 - color_phase * 3) : (color_phase * 3);
  uint8_t color_g = (color_phase < 85) ? (color_phase * 3) : (255 - (color_phase - 85) * 3);
  uint8_t color_b = (color_phase < 170) ? (255 - (color_phase - 85) * 3) : ((color_phase - 170) * 3);

  // 应用亮度变化
  color_r = color_r * brightness / 255;
  color_g = color_g * brightness / 255;
  color_b = color_b * brightness / 255;

  // 设置 LED 颜色
  for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
    ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, color_r, color_g, color_b));
  }
  ESP_ERROR_CHECK(led_strip_refresh(led_strip));

  // 更新亮度
  brightness += direction;
  if (brightness >= 255 || brightness <= 0) {
    direction = -direction;
  }

  // 更新颜色相位
  color_phase += color_step;
  if (color_phase >= 255) {
    color_phase = 0;
  }

  vTaskDelay(pdMS_TO_TICKS(10));
}

void led_rainbow_rotate(led_strip_handle_t led_strip) {
  static uint8_t hue = 0;  // 色调值，用于控制颜色变化
  static uint8_t offset = 0;  // 偏移量，用于控制旋转效果

  // 遍历所有 LED
  for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
    // 计算当前 LED 的颜色
    uint8_t led_hue = hue + (i * 255 / LED_STRIP_LED_COUNT) + offset;
    uint8_t r, g, b;

    // 将色调值转换为 RGB 颜色
    if (led_hue < 85) {
      r = 255 - led_hue * 3;
      g = led_hue * 3;
      b = 0;
    } else if (led_hue < 170) {
      led_hue -= 85;
      r = 0;
      g = 255 - led_hue * 3;
      b = led_hue * 3;
    } else {
      led_hue -= 170;
      r = led_hue * 3;
      g = 0;
      b = 255 - led_hue * 3;
    }

    // 设置 LED 颜色
    ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, r, g, b));
  }

  // 刷新 LED 灯带
  ESP_ERROR_CHECK(led_strip_refresh(led_strip));

  // 更新色调值和偏移量
  hue += 1;  // 控制颜色渐变速度
  offset += 5;  // 控制旋转速度

  // 延时，控制效果刷新频率
  vTaskDelay(pdMS_TO_TICKS(20));
}